package com.zero.manage.web.utils.exception;

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import io.gitee.zerowsh.enums.ResultEnums;
import io.gitee.zerowsh.util.LogTag;
import io.gitee.zerowsh.util.PublicResult;
import io.gitee.zerowsh.util.ZeroException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.ShiroException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 统一处理错误异常
 */
@RestControllerAdvice
@Log4j2
@Api(tags = "全局异常")
public class ExceptionUtil {
    /**
     * 捕捉shiro的异常
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ShiroException.class)
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public PublicResult handle401(ShiroException e) {
        log.error(e);
        return PublicResult.resultEnums(ResultEnums.UNAUTHORIZED, e.getMessage());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public PublicResult mismatchErrorHandler(MethodArgumentTypeMismatchException e) {
        log.error(e);
        return PublicResult.resultEnums(ResultEnums.CONVERT_FAIL);
    }

    /**
     * 捕捉其他所有自定义Shiro异常
     *
     * @return
     */
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler(ZeroException.class)
    public PublicResult exception(ZeroException e) {
        log.error(e);
        return JSONUtil.toBean(e.getMessage(), PublicResult.class);
    }

    /**
     * 所有验证框架异常捕获处理
     *
     * @return
     */
    @ExceptionHandler(value = {BindException.class, MethodArgumentNotValidException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public PublicResult validationExceptionHandler(Exception e) {
        List<FieldError> fieldErrors = new ArrayList<>(10);
        if (e instanceof BindException) {
            fieldErrors = ((BindException) e).getBindingResult().getFieldErrors();
        } else if (e instanceof MethodArgumentNotValidException) {
            fieldErrors = ((MethodArgumentNotValidException) e).getBindingResult().getFieldErrors();
        }
        Map<String, String> map = new HashMap<>(fieldErrors.size());
        for (FieldError error : fieldErrors) {
            map.put(error.getField(), error.getDefaultMessage());
        }
        return PublicResult.resultEnums(ResultEnums.DATA_VERIFICATION, map);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public PublicResult exception(HttpRequestMethodNotSupportedException e) {
        return PublicResult.resultEnums(ResultEnums.NOT_SUPPORTED, e.getMessage());
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public PublicResult exception(MissingServletRequestParameterException e) {
        return PublicResult.resultEnums(ResultEnums.REQUEST_PARAMETER, e.getMessage());
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public PublicResult exception(HttpMessageNotReadableException e) {
        return PublicResult.resultEnums(ResultEnums.MESSAGE_NOT_READABLE, e.getMessage());
    }

    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public PublicResult exception(HttpMediaTypeNotSupportedException e) {
        return PublicResult.resultEnums(ResultEnums.MEDIA_TYPE, e.getMessage());
    }

    /**
     * excel全局异常处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(ExcelAnalysisException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ApiOperation(value = "Excel解析")
    @LogTag
    public PublicResult exception(ExcelAnalysisException e) {
        Throwable cause = e.getCause();
        Object data = e.getMessage();
        if (cause instanceof ExcelDataConvertException) {
            ExcelDataConvertException exception = (ExcelDataConvertException) cause;
            data = String.format("第%s行，第%s列解析异常，异常信息:%s",
                    exception.getRowIndex(),
                    exception.getColumnIndex(),
                    exception.getMessage());
        }
        return PublicResult.resultEnums(ResultEnums.IMPORT_FAIL, data);
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ApiOperation(value = "程序bug")
    @LogTag
    public PublicResult exception(Exception e) {
        log.error(e);
        return PublicResult.resultEnums(ResultEnums.INTERNAL_SERVER_ERROR, e.getMessage());
    }
}
